package fatworm.storage.table;

import java.util.*;

import fatworm.storage.file.*;
import fatworm.tx.*;
import fatworm.util.*;

/*
 * [TAIL, CURMAXID, [PRE, ID, BYTES]..., PRE]
 */
public class VarPage
{
    private static final int INT_SIZE = 4;
    private Block blk;
    private Transaction tx;
    private int curPos = -1;
    private int curLen = -1;

    public VarPage(Block blk, Transaction tx)
    {
        this.blk = blk;
        this.tx = tx;
        tx.pin(blk);
    }
    public void close()
    {
        if (blk != null)
        {
            tx.unpin(blk);
            blk = null;
        }
    }
    public boolean next()
    {
        Lib.debug("VarPage.next()");
        if (curPos == -1)
            curPos = tx.getInt(blk, 0);
        int nxtPos = tx.getInt(blk, curPos);
        if (nxtPos == 0)
            return false;
        Lib.debug(nxtPos + " " + curPos);
        curLen = curPos - nxtPos;
        curPos = nxtPos;
        return true;
    }
    public byte[] getBytes()
    {
        Lib.debug("VarPage.getBytes()" + " curLen=" + curLen);
        return tx.getBytes(blk, curPos + 2 * INT_SIZE, curLen - 2 * INT_SIZE);
    }
    public void setBytes(byte[] val)
    {
        int curMaxID = tx.getInt(blk, INT_SIZE) + 1;
        tx.setInt(blk, INT_SIZE, curMaxID); // update curMaxID
        tx.setInt(blk, curPos + INT_SIZE, curMaxID); // note my ID
        tx.setBytes(blk, curPos + INT_SIZE * 2, val); // write my value
        tx.setInt(blk, curPos + INT_SIZE * 2 + val.length, curPos); // write my
                                                                    // tail
        tx.setInt(blk, 0, curPos + INT_SIZE * 2 + val.length); // update tail
    }
    public boolean insert(int vallen)
    {
        curPos = tx.getInt(blk, 0);
        if (curPos + vallen + 3 * INT_SIZE <= Page.BLOCK_SIZE)
            return true;
        else
            return false;
    }
    public void delete()
    {
        // check next() is called
        if (curPos == -1 || curPos == 0)
            throw new Error("!!!");
        byte[] bytes = tx.getBytes(blk, 0, Page.BLOCK_SIZE); // XXX efficiency?
        int p = Lib.bytesToInt(bytes, 0);
        Lib.bytesFromInt(bytes, 0, p - curLen);
        while (true)
        {
            int nxt = Lib.bytesToInt(bytes, p);
            if (nxt != curPos)
                Lib.bytesFromInt(bytes, p, nxt - curLen);
            else
            {
                Lib.bytesFromInt(bytes, p, Lib.bytesToInt(bytes, curPos));
                break;
            }
        }
        System.arraycopy(bytes, curPos + curLen, bytes, curPos, Page.BLOCK_SIZE - (curPos + curLen));
        Arrays.fill(bytes, Page.BLOCK_SIZE - curLen, Page.BLOCK_SIZE, (byte) 0);
        tx.setBytes(blk, 0, bytes);
    }
    public void moveToId(int id)
    {
        Lib.debug("VarPage.moveToID()" + id);
        byte[] bytes = tx.getBytes(blk, 0, Page.BLOCK_SIZE);
        // for (int i = 0; i < bytes.length; i++)
        // Lib.debug(i + ": " + bytes[i]);
        curPos = -1;
        boolean found = false;
        while (this.next())
        {
            int curId = this.currentId();
            if (curId == id)
            {
                found = true;
                break;
            }
        }
        if (!found)
            throw new Error("!!!");
    }
    public int currentId()
    {
        return tx.getInt(blk, curPos + INT_SIZE);
    }
    public static void main(String[] args)
    {
        // TODO need test
    }
}
